﻿/// <reference path="jquery.js" />
/// <reference path="svg.js" />
/// <reference path="pv.core.js" />
/// <reference path="database.js" />

function PvPoint(x, y) {
    this.x = x;
    this.y = y;
}

function getNode(id, list) {
    //    opts = $.extend({
    //        defaultType: list[0]
    //    }, opts || {});

    var len = list.length;

    for (i = 0; i < len; i++) {
        if (id == list[i].id) {
            return $.extend({}, list[i]);
        }
    }

    return $.extend({}, list[0]);
}

//#region node
function PvNodeProto() {
    this.name = "";
    this.svg = '<circle r="40" stroke="black" stroke-width="2" fill="white"/>';
    this.radius = 60;
    this.dispose = {
        before: function (sender, e) { },
        after: function (sender, e) { }
    };
}

function PvNodeOpt() {
    this.rotation = null;
    this.disposed = function (sender, e) { }
}

function PvNode() {
    //    this.DOM = null;
    //    this.defaultStatus = null;
    //    this.status = null;
    //    this.isAlive = false;
    //    this.dispose = null;

    var self = this;
    this.init = function (name, cat, list, position, opts) {
        position = $.extend(new PvPoint(0, 0), position);
        opts = $.extend(new PvNodeOpt(), opts);

        //self.defaultStatus = getNode(name, list);
        self.status = jQuery.extend({}, getNode(name, list));

        self.DOM = convertStringToSvg(self.status.svg);

        self.isAlive = true;
        self.dispose = function () {
            self.status.dispose.before(this, {});

            self.DOM.remove();
            self.isAlive = false;

            opts.disposed(this, {});
            self.status.dispose.after(this, {});
        };

        self.DOM.attr("pv-type", name);
        self.DOM.attr("pv-cat", cat);

        if (self.status.radius)
            self.DOM.attr("pv-radius", self.status.radius);

        self.DOM.setTranslate(position.x, position.y);
        if (opts.rotation)
            self.DOM.setRotate(opts.rotation);

        pv.svg.append(self.DOM);
    }
}

//#endregion

//#region projectile

function PvProjectileOpt() {
    PvNodeOpt.call(this);
}

function PvProjectileProto() {
    PvNodeProto.call(this);


    this.speed_a = 0.5;
    this.speed_r = 0;
    this.fly_time = 0.3;
    this.windage = 1;
    this.radius = 30;
    this.auto_rotate = false;
    this.spinnable = false;
    this.demage = 10;
    this.land = {
        after: function (sender, e) {
            sender.dispose();
        }
    };
    this.dispose = {
        before: function () { },
        after: function (sender, e) {
            var posi = sender.DOM.getTranslate();
            tip.shatter('<g><image href="img/shatteredstone.png" height="40" width="40" transform="translate(-20,-20)"/></g>', posi.x, posi.y);
        }
    };
    this.attach = function (sender, e) {
        e.targets[0].trigger('hit', [sender, e]);
        sender.dispose();
    };
    this.buff = function (sender, e) {
        return false;
    };

}

var pro = {
    init: function (target, handle, type, mousedown, mousemove, launch, refreshMouseMovedDis) {
        var svg = pv.svg;
        function move(event) {
            if (target.isPicked) {
                var posi = getPosition(event);
                //if (pv.isInBoundary(posi)) {
                if (pv.isInVillage(posi))
                    mousemove(posi);
                else {
                    pro.pickup(target, false, svg);
                    launch();
                }
                // }
                //                else
                //                    pro.pickup(target, false, svg);
            }
        }

        handle.bind("touchstart", function (e) {
            mousedown(e);
            pro.pickup(target, true, svg);
        });

        handle.mousedown(function (e) {
            mousedown(e);
            pro.pickup(target, true, svg);
        });

        handle.bind("touchend", function (e) {
            pro.pickup(target, false, svg);
            launch();
        });

        handle.mouseup(function (e) {
            pro.pickup(target, false, svg);
            launch();
        });

        svg.mouseleave(function (e) {
            pro.pickup(target, false, svg);
            // launch();
        });

        svg.bind("touchmove", function (event) {
            event.preventDefault();
            move(event);
        });

        svg.mousemove(function (event) {
            move(event);
        });


        pv.setTimer(target, null, refreshMouseMovedDis);
    },

    calSpeed: function (diff, speed_r) {
        if (speed_r > 0) {
            var r = geo.vectorNorm(diff);
            if (Math.abs(r.x * speed_r) < Math.abs(diff.x) && Math.abs(r.y * speed_r) < Math.abs(diff.y))
                diff = { x: diff.x - r.x * speed_r, y: diff.y - r.y * speed_r };
            else diff = { x: 0, y: 0 };
        }
        return diff;
    },

    pickup: function (target, picked, svg, opts) {
        opts = $.extend({
            animate: function (picked) {
                if (picked) {
                    //target.svgAddClass("shadow");
                    //target.setScale(1.2);
                }
                else {
                    // target.svgRemoveClass("shadow");
                    //target.DOM.setScale(1);
                }
            }
        }, opts || {});

        target.isPicked = picked;
        if (picked) {
            //svg.append(target);
            target.DOM.attr("pv-picked", "picked");
            //if (animate)
            opts.animate(picked);
        }
        else {
            target.DOM.removeAttr("pv-picked");
            opts.animate(picked);
        }
    },

    detectTarget: function (target, currentCentre) {
        // target = $.extend(new PvProjectile(), target);

        var detectedTargets = [];
        pv.svg.find("[pv-cat='target']").each(function () {
            var targetRadius = $(this).attr("pv-radius");
            if (targetRadius) {
                targetRadius = parseFloat(targetRadius);
                targetPosi = $(this).getTranslate();

                if (geo.dist2p(targetPosi, currentCentre) - target.status.radius - targetRadius < 0)
                    detectedTargets.push($(this));
            }
        });

        if (detectedTargets.length > 0) {
            //type.effect(target, { projectile: type, targets: detectedTargets });
            target.status.attach(target, { projectile: target.status, targets: detectedTargets }); //type.demage, target, detectedTargets);
        }
    }
};

function PvProjectile(name, position, opts) {
    position = $.extend(new PvPoint(), position);
    opts = $.extend(new PvProjectileOpt(), opts);

    PvNode.call(this);
    this.init(name, "projectile", projectiles, position, opts);

    this.status = $.extend(new PvProjectileProto(), this.status);

    var self = this;

    var currentCentre = position;
    var preCentre = currentCentre;
    var preCursorPosi = currentCentre;
    var mouseMovedDis = null;

    var currentRotation = 0;

    var currentSpinAngle = 0;
    var spinInterval = null;

    var launch = function () {
        var sec = self.status.fly_time;

        var diff = { x: mouseMovedDis.x, y: mouseMovedDis.y };
        pv.setTimer(self, sec, function () {
            if (!pv.isInBoundary(currentCentre)) {
                pro.pickup(self, false, pv.svg);

                return true;
            }

            if (self.status.spinnable) {
                var diffOff = 0;
                if (currentSpinAngle < 0)
                    diffOff = geo.vectorNorm(geo.vectorPerpLeft(diff));
                else if (currentSpinAngle > 0)
                    diffOff = geo.vectorNorm(geo.vectorPerpRight(diff));
                if (currentSpinAngle != 0) {
                    var spin_off = Math.abs(currentSpinAngle * type.spin_off);
                    diff = geo.vectorAdd(diff, { x: diffOff.x * spin_off, y: diffOff.y * spin_off });
                }
            }

            diff = pro.calSpeed(diff, self.status.speed_r);

            currentCentre = { x: (currentCentre.x + diff.x + pv.wind.x * self.status.windage), y: (currentCentre.y + diff.y + pv.wind.y * self.status.windage) };
            self.DOM.setTranslate(currentCentre.x, currentCentre.y);

            pro.detectTarget(self, currentCentre);

        }, function () {
            if (!pv.isInVillage(currentCentre)) {
                self.status.land.after(self, {});
            }
        });
    };

    function mousedown(e) {
        preCursorPosi = getPosition(e);
    }

    function mousemove(posi) {
        var dis = { x: (posi.x - preCursorPosi.x), y: (posi.y - preCursorPosi.y) };
        currentCentre = { x: (currentCentre.x + dis.x), y: (currentCentre.y + dis.y) };

        if (self.status.spinnable) {
            var offset = svg.offset();
            var angle = geo.angle3p(currentCentre, preCursorPosi, posi);

            if (angle) {
                if (currentSpinAngle * angle < 0 || Math.abs(currentSpinAngle) < Math.abs(angle * type.spin_a)) {
                    currentSpinAngle = angle * type.spin_a;
                    if (spinInterval)
                        clearInterval(spinInterval);

                    spinInterval = setInterval(function () {
                        var rot = target.getRotate();
                        rot = target.getRotate();
                        target.setRotate(rot + currentSpinAngle);
                        if (Math.abs(currentSpinAngle) >= type.spin_r) {
                            if (angle > 0) {
                                currentSpinAngle -= type.spin_r;
                            }
                            else
                                currentSpinAngle += type.spin_r;
                        }
                        else {
                            currentSpinAngle = 0;
                            clearInterval(spinInterval);
                            spinInterval = null;
                        }
                    }, pv.frameInterval);
                }
            }
        }

        self.DOM.setTranslate(currentCentre.x, currentCentre.y);

        if (self.status.auto_rotate) {
            var angle = geo.vectorDir(mouseMovedDis);
            if (angle) {
                target.setRotate(angle);
            }
        }

        preCursorPosi = posi;
    }

    //detect speed
    function refreshMouseMovedDis() {
        if (preCentre != null)
            mouseMovedDis = { x: currentCentre.x - preCentre.x, y: currentCentre.y - preCentre.y };
        preCentre = currentCentre;
    }

    pro.init(self, self.DOM, self.defaultStatus, mousedown, mousemove, launch, refreshMouseMovedDis);
}

function PvChainProjectileProto() {
    PvProjectileProto.call(this);

    this.dispose = {
        after: function (sender, e) {
            var posi = sender.DOM.getTranslate();
            var handle = sender.DOM.find('[pv-chain="last"]').getTranslate();
            tip.shatter('<g><image href="img/shatteredstone.png" height="40" width="40" transform="translate(-20,-20)"/></g>', posi.x + handle.x, posi.y + handle.y);
        },
        before: function (sender, e) { }
    };

    this.handle_offset = 40;
    this.payload_offset = 30;
    this.chain_num = 4;
    this.chain_len = 22;
    this.auto_rotate = true;
}

function PvChainProjectile(name, position, opts) {
    position = $.extend(new PvPoint(), position);
    opts = $.extend(new PvProjectileOpt(), opts);

    PvNode.call(this);
    this.init(name, "projectile", chainProjectiles, position, opts);

    this.status = $.extend(new PvChainProjectileProto(), this.status);

    var self = this;

    //-------------------------------
    var handle = self.DOM.find("[pv-chain='0']");
    var payload = self.DOM.find("[pv-chain='last']");

    var preCentre = null;
    var currentCentre = handle.getTranslate();
    var mouseMovedDis = null;

    var prePayLoadCentre = null;
    var currentPayLoadCentre = payload.getTranslate();
    var payloadMovedDis = null;

    var preCursorPosi = null;
    var currentRotation = 0;

    function launch() {
        sec = self.status.fly_time;

        if (payloadMovedDis) {
            //node.setScale(0.9);

            var diff = payloadMovedDis;

            var angle = geo.vectorDir(diff)
            //payload.setRotate(angle);

            var radianangle = angle * Math.PI / 180;

            pv.setTimer(self, sec, function () {
                if (!pv.isInBoundary({ x: currentPayLoadCentre.x + position.x, y: currentPayLoadCentre.y + position.y }) || self.isPicked && pv.isInVillage({ x: currentPayLoadCentre.x + position.x, y: currentPayLoadCentre.y + position.y })) {
                    prePayLoadCentre = null;
                    currentCentre = handle.getTranslate();
                    return true;
                }

                diff = pro.calSpeed(diff, self.status.speed_r);
                currentPayLoadCentre = { x: (currentPayLoadCentre.x + diff.x + pv.wind.x * self.status.windage), y: (currentPayLoadCentre.y + diff.y + pv.wind.y * self.status.windage) };
                payload.setTranslate(currentPayLoadCentre.x, currentPayLoadCentre.y);
                if (angle)
                    payload.setRotate(angle);
                var x1 = currentPayLoadCentre.x + self.status.payload_offset * Math.sin(-radianangle);
                var y1 = currentPayLoadCentre.y + self.status.payload_offset * Math.cos(-radianangle);

                updateChainPosition(x1, y1, handle, self.status.handle_offset, false, false);

                pro.detectTarget(self, { x: currentPayLoadCentre.x + position.x, y: currentPayLoadCentre.y + position.y });

            }, function () {
                if (!pv.isInVillage({ x: currentPayLoadCentre.x + position.x, y: currentPayLoadCentre.y + position.y }))
                    self.dispose();
            });
        }
    };

    function updateChainPosition(x1, y1, last, last_offset, isToward, isUpdatePayloadPosi) {
        function updateChain(ci) {
            var chain = self.DOM.find("[pv-chain='" + ci + "']");

            if (isToward) {
                var x2 = parseFloat(chain.attr("x2"));
                var y2 = parseFloat(chain.attr("y2"));
            }
            else {
                var x2 = parseFloat(chain.attr("x1"));
                var y2 = parseFloat(chain.attr("y1"));
            }

            var angle2 = geo.vectorDir({ x: x2 - x1, y: y2 - y1 });
            var radianangle2 = angle2 * Math.PI / 180;

            var x2 = x1 + self.status.chain_len * Math.sin(radianangle2);
            var y2 = y1 - self.status.chain_len * Math.cos(radianangle2);

            if (isToward)
                chain.attr({ x1: x1, y1: y1, x2: x2, y2: y2 });
            else
                chain.attr({ x2: x1, y2: y1, x1: x2, y1: y2 });

            x1 = x2;
            y1 = y2;
        }
        if (isToward) {
            for (var ci = 1; ci <= self.status.chain_num; ci++) {
                updateChain(ci);
            }
        }
        else {
            for (var ci = self.status.chain_num; ci >= 1; ci--) {
                updateChain(ci);
            }
        }

        lastPosi = { x: x1, y: y1 };

        var currentLastCentre = last.getTranslate(); //{x:0,y:0}; //

        if (isUpdatePayloadPosi)
            currentPayLoadCentre = currentLastCentre;

        var lastAngle = geo.vectorDir({ x: currentLastCentre.x - lastPosi.x, y: currentLastCentre.y - lastPosi.y });

        radianLastAngle = lastAngle * Math.PI / 180;

        var lastX = lastPosi.x + last_offset * Math.sin(radianLastAngle);
        var lastY = lastPosi.y - last_offset * Math.cos(radianLastAngle);

        last.setTranslate(lastX, lastY);
        last.setRotate(lastAngle);

        return { x: x1, y: y1 };
    }

    function mousemove(posi) {
        var dis = { x: (posi.x - preCursorPosi.x), y: (posi.y - preCursorPosi.y) };
        currentCentre = { x: (currentCentre.x + dis.x), y: (currentCentre.y + dis.y) };

        handle.setTranslate(currentCentre.x, currentCentre.y);
        var angle = null;
        //if (type.auto_rotate) {
        angle = geo.vectorDir(mouseMovedDis);
        if (angle) {
            handle.setRotate(angle);
        }
        //}

        var radianangle = angle * Math.PI / 180;
        var x1 = currentCentre.x + self.status.handle_offset * Math.sin(-radianangle);
        var y1 = currentCentre.y + self.status.handle_offset * Math.cos(-radianangle);

        updateChainPosition(x1, y1, payload, self.status.payload_offset, true, true);

        //        if (node.isPicked && !pv.isInVillage(posi)) {
        //            pro.pickup(node, false, $target);
        //            launch(type.fly_time);
        //        }
        preCursorPosi = posi;


    }

    //detect speed
    function refreshMouseMovedDis() {
        if (preCentre != null)
            mouseMovedDis = { x: currentCentre.x - preCentre.x, y: currentCentre.y - preCentre.y };
        preCentre = currentCentre;

        if (prePayLoadCentre != null && currentPayLoadCentre != null)
            payloadMovedDis = { x: currentPayLoadCentre.x - prePayLoadCentre.x, y: currentPayLoadCentre.y - prePayLoadCentre.y };


        prePayLoadCentre = currentPayLoadCentre;
    }

    //    handle.mouseleave(function () {
    //        pro.pickup(node, false, $target);
    //    });

    pro.init(self, handle, self.status, function (e) {
        preCursorPosi = getPosition(e); prePayLoadCentre = currentPayLoadCentre;
    }, mousemove, launch, refreshMouseMovedDis);
}

//#endregion

//#region target
function PvTargetProto() {
    PvNodeProto.call(this);

    this.hp_max = 0;
    this.speed = 0.5;
    this.armor = 1;
    this.demage = 20;
    this.attack_interval = 1.5;
}


function PvTargetOpt() {
    PvNodeOpt.call(this);

    this.attached = function (sender, e) { };
}


function PvBuffType() {
    this.id = "";
    this.effect = function (sender, e) {
    };
    this.animate = {
        begin: function (sender, e) { },
        end: function (sender, e) { }
    };
}

//PvTarget.extends(PvNode);
function PvTarget(name, position, opts) {
    position = $.extend(new PvPoint(0, 0), position);
    opts = $.extend(new PvTargetOpt(), opts);

    PvNode.call(this);

    this.init(name, 'target', targets, position, opts);

    this.status = $.extend(new PvTargetProto(), this.status);
    this.defaultStatus = $.extend(new PvTargetProto(), this.status);

    this.hp = this.defaultStatus.hp_max;

    var self = this;

    //attached
    this.DOM.bind('hit', function (event, sender, e) {
        //sender = $.extend(new PvProjectile(), sender);
        var realDemage = -sender.status.demage + self.status.armor;
        realDemage = self.hp > realDemage ? realDemage : self.hp;
        if (realDemage < 0) {
            self.hp += realDemage;
            tip.sound("sound/h2.mp3");

            tip.fadeOut(realDemage, self);

            if (self.hp <= 0) {
                self.dispose();
                return;
            }
        }

        var buff = sender.status.buff(self, e);
        if (buff) {
            effect(buff);
        }
    });

    //buff
    var buffqueue = [];

    var removeBuff = function (id) {
        var bl = buffqueue.length;
        var buff = $.extend({}, self.defaultStatus);
        for (var i = 0; i < bl; i++) {
            if (buffqueue[i].id == id)
                buffqueue.splice(i, 1);
            else {
                buff = $.extend(buff, buffqueue[i]);
            }
        }

        return buff;
    }

    function effect(e) {
        var ff = getNode(e.id, buffs);
        var buffType = $.extend(new PvBuffType(), ff);

        var newBuff = buffType.effect(self, {});

        self.status = $.extend(self.status, newBuff);
        var animateNode = buffType.animate.begin(self, {});

        newBuff.id = e.id;
        buffqueue.push(newBuff);

        pv.setInterval(function () {
            self.status = removeBuff(e.id);
            buffType.animate.end(self, { node: animateNode });
        }, e.last, { times: 1 });
    };

    //#region move
    pv.setTimer(self, null, function () {
        var rotation = pv.attachPath(self);
        var radianrotation = rotation * Math.PI / 180;
        var diff = { x: Math.sin(radianrotation), y: (radianrotation == 0 ? -1 : Math.cos(radianrotation) * -1) };
        diff = geo.vectorNorm(diff);
        var direction = { x: self.status.speed * diff.x, y: self.status.speed * diff.y };
        //        var posi = self.DOM.getTranslate();
        position.x = position.x + direction.x;
        position.y = position.y + direction.y;

        if (pv.isInAttchRange(position, self.status.radius)) {
            pv.setInterval(function () {
                if (self.isAlive)
                    opts.attacked(self.status.demage, self);
                else
                    return true;
            }, self.status.attack_interval);
            return true;
        }
        else {
            self.DOM.setTranslate(position.x, position.y);
            self.DOM.setRotate(rotation);
        }
    });
    //#endregion
    //var node = this.parent
};


//#endregion




